#ifndef __GLF_CORE_READWRITESPINLOCK_H_INCLUDED__
#define __GLF_CORE_READWRITESPINLOCK_H_INCLUDED__

#include <glf/config.h>
#include <glf/core/thread.h>
#include <glf/core/inlines.h>

namespace glf
{

class ReadWriteSpinLock : private NonCopyable
{
public:	
	ReadWriteSpinLock();

	int readLock(unsigned int waitTime = 0);
	int writeLock(unsigned int waitTime = 0);

	int tryReadLock();
	int tryWriteLock();

	int readUnlock();
	int writeUnlock();

	bool hasReadLock() const;
	bool hasWriteLock() const;

	int getOwner() const;

private:

	// Impl are not backward compatible: a waitTime of 0 will not wait.
	// Use readLock or writeLock for backward compatibility.
	int readLockImpl(unsigned int waitTime);
	int writeLockImpl(unsigned int waitTime);

	uchar LockArray[GLF_MAX_THREADS];
	uchar PromoteCount[GLF_MAX_THREADS];
	volatile int Owner;
	uchar WriterCount;
};

inline int ReadWriteSpinLock::readLock(unsigned int waitTime) {
	return readLockImpl(waitTime ? waitTime : (unsigned int)-1);
}

inline int ReadWriteSpinLock::writeLock(unsigned int waitTime) {
	return writeLockImpl(waitTime ? waitTime : (unsigned int)-1);
}

inline int ReadWriteSpinLock::tryReadLock() {
	return readLockImpl(0);
}

inline int ReadWriteSpinLock::tryWriteLock() {
	return writeLockImpl(0);
}

class ScopedReadSpinLock : private NonCopyable
{
public:
	ScopedReadSpinLock(ReadWriteSpinLock &lock)
		: Lock(lock) {
		Lock.readLock();
	};

	~ScopedReadSpinLock() {
		Lock.readUnlock();
	};

	ReadWriteSpinLock &Lock;
};

class ScopedWriteSpinLock : private NonCopyable
{
public:
	ScopedWriteSpinLock(ReadWriteSpinLock &lock)
		: Lock(lock) {
		Lock.writeLock();
	};

	~ScopedWriteSpinLock() {
		Lock.writeUnlock();
	};

	ReadWriteSpinLock &Lock;
};

class PromoteScopedWriteSpinLock : private NonCopyable
{
public:
	PromoteScopedWriteSpinLock(ScopedReadSpinLock& scopedLock)
		: Lock(scopedLock.Lock) {
		Lock.readUnlock();
		Lock.writeLock();
	}

	~PromoteScopedWriteSpinLock() {
		Lock.writeUnlock();
		Lock.readLock();
	}

	ReadWriteSpinLock& Lock;
};

} //namespace glf

#endif //__GLF_CORE_READWRITESPINLOCK_H_INCLUDED__
